
const messages = document.getElementById('messages');
const messageForm = document.getElementById('messageForm');
const messageInput = document.getElementById('messageInput');
let username = '';

function setChatVisibility(visible) {
  messages.style.display = visible ? 'block' : 'none';
  messageForm.style.display = visible ? 'flex' : 'none';
}

function startChat() {
  setChatVisibility(true);
  const socket = io();

  // Load message history via HTTP (requires authentication)
  fetch('/history').then(r => {
    if (!r.ok) return [];
    return r.json();
  }).then(history => {
    messages.innerHTML = '';
    history.forEach(msg => {
      if (msg.message === '[已撤回]') return; // skip retracted messages
      displayMessage(msg.username, msg.message, msg.timestamp, msg.id);
    });
  }).catch(() => {
    // ignore errors; history will be empty
  });

  // When socket connects, send identity so server can track online users
  socket.on('connect', () => {
    if (username) socket.emit('identify', username);
  });

  // Update online user list UI
  socket.on('online users', (list) => {
    const panel = document.getElementById('onlineUsers');
    if (!panel) return;
    panel.textContent = list.join(', ');
  });

  // Receive new messages
  socket.on('chat message', (data) => {
    if (data.message === '[已撤回]') return; // don't render retracted
    displayMessage(data.username, data.message, data.timestamp, data.id);
  });

  // Receive edited messages
  socket.on('message edited', (data) => {
    applyEditedMessage(data.id, data.message);
  });

  // Receive retracted messages
  socket.on('message retracted', (data) => {
    applyRetractedMessage(data.id);
  });

  // Send message
  messageForm.addEventListener('submit', (e) => {
    e.preventDefault();
    const message = messageInput.value;
    if (message) {
      socket.emit('chat message', { username, message });
      messageInput.value = '';
    }
  });
}

function displayMessage(sender, message, timestamp, id) {
  const messageElement = document.createElement('div');
  messageElement.classList.add('message');
  if (id !== undefined && id !== null) messageElement.setAttribute('data-id', id);
  if (sender) messageElement.setAttribute('data-author', sender);
  // Mark message as 'me' or 'other' comparing sender to global username
  if (sender && username && sender === username) {
    messageElement.classList.add('me');
  } else {
    messageElement.classList.add('other');
  }
  // mark deepseek messages so we can style them and allow special actions
  if (sender && sender.toLowerCase() === 'deepseek') {
    messageElement.classList.add('deepseek');
  }
  // Format time in Chinese locale for users (full timestamp)
  const time = new Date(timestamp).toLocaleString('zh-CN', { hour12: false });
  // Minimal geek-style layout: [meta] + bubble text
  messageElement.innerHTML = `
    <div class="meta">${escapeHtml(sender)} [${escapeHtml(time)}]</div>
    <div class="bubble" data-content>${escapeHtml(message)}</div>
  `;
  // If this message is authored by current user, make it editable via geek UI
  const bubble = messageElement.querySelector('[data-content]');
  // Author can edit/retract; additionally allow anyone to interact with deepseek messages to retract
  if (sender && username && sender === username) {
    messageElement.classList.add('me');
    bubble.style.cursor = 'pointer';
    bubble.title = '点击以操作（编辑 / 撤回）';
    bubble.addEventListener('click', (e) => showActionMenu(e, messageElement, bubble));
  } else if (sender && sender.toLowerCase() === 'deepseek') {
    // make deepseek messages clickable for retract by any logged-in user, but no edit
    bubble.style.cursor = 'pointer';
    bubble.title = '点击以操作（撤回）';
    bubble.addEventListener('click', (e) => showActionMenu(e, messageElement, bubble));
  }
  messages.appendChild(messageElement);
  messages.scrollTop = messages.scrollHeight;
}

// Inline geek editor: replace bubble with a small terminal-like input and action buttons
function startInlineEdit(messageElement, bubble) {
  // prevent multiple editors
  if (messageElement.querySelector('.geek-edit')) return;
  const orig = bubble.textContent;
  const id = messageElement.getAttribute('data-id');
  const editor = document.createElement('div');
  editor.className = 'geek-edit';
  editor.style.display = 'flex';
  editor.style.gap = '8px';
  editor.style.alignItems = 'center';
  editor.style.marginTop = '6px';
  editor.innerHTML = `
    <input class="geek-input" value="${escapeHtml(orig)}" style="flex:1;padding:6px;background:#071; color:#cfc; border:1px solid rgba(57,255,20,0.12);border-radius:4px">
    <button class="geek-save" style="padding:6px 8px;font-size:0.85rem">保存</button>
    <button class="geek-cancel" style="padding:6px 8px;font-size:0.85rem">取消</button>
  `;
  bubble.style.display = 'none';
  messageElement.appendChild(editor);

  const input = editor.querySelector('.geek-input');
  const save = editor.querySelector('.geek-save');
  const cancel = editor.querySelector('.geek-cancel');

  cancel.addEventListener('click', () => {
    editor.remove();
    bubble.style.display = '';
  });

  save.addEventListener('click', async () => {
    const newText = input.value.trim();
    if (newText === orig) { cancel.click(); return; }
    // optimistic UI: show a small inline status
  save.disabled = true; save.textContent = '保存中...';
    try {
      const res = await fetch('/message/edit', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ id, newText })
      });
      const j = await res.json();
      if (res.ok && j.ok) {
        // apply change locally (server will broadcast to others too)
        bubble.textContent = newText;
        editor.remove();
        bubble.style.display = '';
      } else {
        alert('编辑失败: ' + (j.error || res.statusText));
        save.disabled = false; save.textContent = '保存';
      }
        } catch (err) {
      console.error(err);
      alert('网络错误，保存失败');
      save.disabled = false; save.textContent = '保存';
    }
  });
}

// Show action menu: Edit / Retract / Cancel
function showActionMenu(e, messageElement, bubble) {
  // remove existing menus
  document.querySelectorAll('.action-menu').forEach(n => n.remove());
  const id = messageElement.getAttribute('data-id');
  const menu = document.createElement('div');
  menu.className = 'action-menu';
  menu.style.position = 'absolute';
  menu.style.zIndex = 1000;
  menu.style.background = '#041';
  menu.style.border = '1px solid rgba(57,255,20,0.12)';
  menu.style.padding = '6px';
  menu.style.borderRadius = '6px';
  menu.style.boxShadow = '0 6px 18px rgba(0,0,0,0.5)';
  // determine whether to show Edit (only if current user is the author)
  const author = messageElement.getAttribute('data-author');
  const showEdit = (author && username && author === username);
  menu.innerHTML = `
    ${showEdit ? '<button class="menu-edit" style="display:block;padding:6px 10px;margin-bottom:6px;font-size:0.85rem">编辑</button>' : ''}
    <button class="menu-retract" style="display:block;padding:6px 10px;margin-bottom:6px;font-size:0.85rem">撤回</button>
    <button class="menu-cancel" style="display:block;padding:6px 10px;font-size:0.85rem">取消</button>
  `;
  document.body.appendChild(menu);
  // position near click
  const rect = bubble.getBoundingClientRect();
  menu.style.left = (rect.right - 8) + 'px';
  menu.style.top = (rect.top - 6) + 'px';

  const cancelBtn = menu.querySelector('.menu-cancel');
  if (cancelBtn) cancelBtn.addEventListener('click', () => menu.remove());
  const editBtn = menu.querySelector('.menu-edit');
  if (editBtn) {
    editBtn.addEventListener('click', () => {
      menu.remove();
      startInlineEdit(messageElement, bubble);
    });
  }
  const retractBtn = menu.querySelector('.menu-retract');
  if (retractBtn) {
    retractBtn.addEventListener('click', async () => {
      // send retract immediately without confirmation
      try {
        const res = await fetch('/message/retract', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ id }) });
        const j = await res.json();
        if (res.ok && j.ok) {
          // local update will be via socket, but update immediately for responsiveness
          applyRetractedMessage(id);
          menu.remove();
        } else {
          alert('撤回失败: ' + (j.error || res.statusText));
        }
      } catch (err) {
        console.error(err);
        alert('网络错误，撤回失败');
      }
    });
  }

  // hide menu on outside click
  const onDocClick = (ev) => { if (!menu.contains(ev.target)) { menu.remove(); document.removeEventListener('click', onDocClick); } };
  setTimeout(() => document.addEventListener('click', onDocClick), 0);
}

function applyRetractedMessage(id) {
  const el = Array.from(messages.querySelectorAll('.message')).find(m => m.getAttribute('data-id') === String(id));
  if (!el) return;
  el.remove();
}

// Update message text when server broadcasts an edit
function applyEditedMessage(id, newText) {
  const el = Array.from(messages.querySelectorAll('.message')).find(m => m.getAttribute('data-id') === String(id));
  if (!el) return;
  const bubble = el.querySelector('[data-content]');
  if (bubble) bubble.textContent = newText;
}

// Simple HTML escape to avoid injection
function escapeHtml(s) {
  return String(s).replace(/[&<>"']/g, function (c) {
    return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":"&#39;"}[c];
  });
}

window.addEventListener('DOMContentLoaded', () => {
  // Wait for username to be set
  const usernameInput = document.getElementById('username');
  if (usernameInput.value) {
    username = usernameInput.value;
    startChat();
  } else {
    setChatVisibility(false);
    fetch('/whoami').then(r => r.json()).then(data => {
      username = data.username;
      usernameInput.value = username;
      if (username) startChat();
      else setChatVisibility(false);
    });
  }
});
